home *** CD-ROM | disk | FTP | other *** search
/ MacWorld 2003 August / MW 8 2003 CD1.iso / Inside Macworld / Product News / gimp-1.2.4.sit / gimp-1.2.4 / app / scan_convert.c < prev    next >
Encoding:
C/C++ Source or Header  |  2001-12-29  |  7.7 KB  |  351 lines

  1. /* The GIMP -- an image manipulation program
  2.  * Copyright (C) 1995-1999 Spencer Kimball and Peter Mattis
  3.  *
  4.  * This program is free software; you can redistribute it and/or modify
  5.  * it under the terms of the GNU General Public License as published by
  6.  * the Free Software Foundation; either version 2 of the License, or
  7.  * (at your option) any later version.
  8.  *
  9.  * This program is distributed in the hope that it will be useful,
  10.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.  * GNU General Public License for more details.
  13.  *
  14.  * You should have received a copy of the GNU General Public License
  15.  * along with this program; if not, write to the Free Software
  16.  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  17.  */
  18.  
  19. #include "config.h"
  20.  
  21. #include <glib.h>
  22.  
  23. #include "gimpimage.h"
  24. #include "scan_convert.h"
  25. #include "libgimp/gimpmath.h"
  26.  
  27. #include <string.h>
  28.  
  29. #ifdef DEBUG
  30. #define TRC(x) printf x
  31. #else
  32. #define TRC(x)
  33. #endif
  34.  
  35.  
  36. /* Reveal our private structure */
  37. struct ScanConverterPrivate
  38. {
  39.     guint    width;
  40.     guint    height;
  41.     GSList **scanlines;        /* array of height*antialias scanlines */
  42.  
  43.     guint    antialias;        /* how much to oversample by */
  44.  
  45.     /* record the first and last points so we can close the curve */
  46.     gboolean got_first;
  47.     ScanConvertPoint first;
  48.     gboolean got_last;
  49.     ScanConvertPoint last;
  50. };
  51.  
  52.  
  53.  
  54. /* Local helper routines to scan convert the polygon */
  55.  
  56. static GSList *
  57. insert_into_sorted_list (GSList *list,
  58.              int     x)
  59. {
  60.   GSList *orig = list;
  61.   GSList *next;
  62.  
  63.   if (!list)
  64.     return g_slist_prepend (list, GINT_TO_POINTER (x));
  65.  
  66.   while (list)
  67.     {
  68.       next = g_slist_next (list);
  69.       if (x < GPOINTER_TO_INT (list->data))
  70.     {
  71.       next = g_slist_prepend (next, list->data);
  72.       list->next = next;
  73.       list->data = GINT_TO_POINTER (x);
  74.       return orig;
  75.     }
  76.       else if (!next)
  77.     {
  78.       g_slist_append (list, GINT_TO_POINTER (x));
  79.       return orig;
  80.     }
  81.       list = g_slist_next (list);
  82.     }
  83.  
  84.   return orig;
  85. }
  86.  
  87.  
  88. static void
  89. convert_segment (ScanConverter *sc,
  90.                  gint           x1,
  91.                  gint           y1,
  92.                  gint           x2,
  93.                  gint           y2)
  94. {
  95.     gint     ydiff, y;
  96.     gint     width;
  97.     gint     height;
  98.     gfloat   xinc, x;
  99.     GSList **scanlines;
  100.  
  101.     /* pre-calculate invariant commonly used values */
  102.     width     = sc->width  * sc->antialias;
  103.     height    = sc->height * sc->antialias;
  104.     scanlines = sc->scanlines;    
  105.  
  106.     ydiff = y2 - y1;
  107.  
  108.     if (ydiff)
  109.       {
  110.         if (ydiff < 0)
  111.           {
  112.             gint tmp;
  113.  
  114.             tmp = y2; y2 = y1; y1 = tmp;
  115.             tmp = x2; x2 = x1; x1 = tmp;
  116.  
  117.             ydiff = - ydiff;
  118.           }
  119.  
  120.     xinc = (float) (x2 - x1) / (float) ydiff;
  121.     x = x1 + 0.5 * xinc;
  122.  
  123.     for (y = y1 ; y < y2; y++)
  124.           {
  125.             if (y >= 0 && y < height)
  126.               scanlines[y] = insert_into_sorted_list (scanlines[y],
  127.                                                       CLAMP (ROUND (x), 
  128.                                                              0, width));
  129.  
  130.             x += xinc;
  131.           }
  132.       }
  133. }
  134.  
  135.  
  136.  
  137. /**************************************************************/
  138. /* Exported functions */
  139.  
  140.  
  141. /* Create a new scan conversion context */
  142. ScanConverter *
  143. scan_converter_new (guint width, guint height, guint antialias)
  144. {
  145.     ScanConverter *sc;
  146.  
  147.     g_return_val_if_fail (width > 0, NULL);
  148.     g_return_val_if_fail (height > 0, NULL);
  149.     g_return_val_if_fail (antialias > 0, NULL);
  150.  
  151.     sc = g_new0 (ScanConverter, 1);
  152.  
  153.     sc->antialias = antialias;
  154.     sc->width  = width;
  155.     sc->height = height;
  156.     sc->scanlines = g_new0 (GSList *, height*antialias);
  157.  
  158.     return sc;
  159. }
  160.  
  161.  
  162. /* Add "npoints" from "pointlist" to the polygon currently being
  163.  * described by "scan_converter".  */
  164. void
  165. scan_converter_add_points (ScanConverter *sc,
  166.                guint npoints,
  167.                ScanConvertPoint *pointlist)
  168. {
  169.     int i;
  170.     guint antialias;
  171.  
  172.     g_return_if_fail (sc != NULL);
  173.     g_return_if_fail (pointlist != NULL);
  174.  
  175.     antialias = sc->antialias;
  176.  
  177.     if (!sc->got_first && npoints > 0)
  178.     {
  179.     sc->got_first = TRUE;
  180.     sc->first = pointlist[0];
  181.     }
  182.  
  183.     /* link from previous point */
  184.     if (sc->got_last && npoints > 0)
  185.     {
  186.     TRC (("|| %g,%g -> %g,%g\n",
  187.           sc->last.x, sc->last.y,
  188.           pointlist[0].x, pointlist[0].y));
  189.     convert_segment (sc,
  190.              (int)sc->last.x * antialias,
  191.              (int)sc->last.y * antialias,
  192.              (int)pointlist[0].x * antialias,
  193.              (int)pointlist[0].y * antialias);
  194.     }
  195.  
  196.     for (i = 0; i < (npoints - 1); i++)
  197.     {
  198.     convert_segment (sc,
  199.              (int) pointlist[i].x * antialias,
  200.              (int) pointlist[i].y * antialias,
  201.              (int) pointlist[i + 1].x * antialias,
  202.              (int) pointlist[i + 1].y * antialias);
  203.     }
  204.  
  205.     TRC (("[] %g,%g -> %g,%g\n",
  206.       pointlist[0].x, pointlist[0].y,
  207.       pointlist[npoints-1].x, pointlist[npoints-1].y));
  208.  
  209.     if (npoints > 0)
  210.     {
  211.     sc->got_last = TRUE;
  212.     sc->last = pointlist[npoints - 1];
  213.     }
  214. }
  215.  
  216.  
  217.  
  218. /* Scan convert the polygon described by the list of points passed to
  219.  * scan_convert_add_points, and return a channel with a bits set if
  220.  * they fall within the polygon defined.  The polygon is filled
  221.  * according to the even-odd rule.  The polygon is closed by
  222.  * joining the final point to the initial point. */
  223. Channel *
  224. scan_converter_to_channel (ScanConverter *sc,
  225.                GimpImage *gimage)
  226. {
  227.     Channel *mask;
  228.     GSList *list;
  229.     PixelRegion maskPR;
  230.     guint widtha;
  231.     guint heighta;
  232.     guint antialias;
  233.     guint antialias2;
  234.     unsigned char *buf, *b;
  235.     int * vals, val;
  236.     int x, w;
  237.     int i, j;
  238.  
  239.     antialias = sc->antialias;
  240.     antialias2 = antialias * antialias;
  241.  
  242.     /*  do we need to close the polygon? */
  243.     if (sc->got_first && sc->got_last &&
  244.     (sc->first.x != sc->last.x || sc->first.y != sc->last.y))
  245.     {
  246.     convert_segment (sc,
  247.              (int) sc->last.x * antialias,
  248.              (int) sc->last.y * antialias,
  249.              (int) sc->first.x * antialias,
  250.              (int) sc->first.y * antialias);
  251.     }
  252.  
  253.     mask = channel_new_mask (gimage, sc->width, sc->height);
  254.  
  255.     buf = g_new0 (unsigned char, sc->width);
  256.     widtha  = sc->width * antialias;
  257.     heighta = sc->height * antialias;
  258.     /* allocate value array  */
  259.     vals = g_new (int, widtha);
  260.  
  261.     /* dump scanlines */
  262.     for(i=0; i<heighta; i++)
  263.     {
  264.     list = sc->scanlines[i];
  265.     TRC (("%03d: ", i));
  266.     while (list)
  267.     {
  268.         TRC (("%3d ", GPOINTER_TO_INT (list->data)));
  269.         list = g_slist_next (list);
  270.     }
  271.     TRC (("\n"));
  272.     }
  273.  
  274.     pixel_region_init (&maskPR, drawable_data (GIMP_DRAWABLE(mask)), 0, 0, 
  275.                drawable_width (GIMP_DRAWABLE(mask)), 
  276.                drawable_height (GIMP_DRAWABLE(mask)), TRUE);
  277.  
  278.     for (i = 0; i < heighta; i++)
  279.     {
  280.     list = sc->scanlines[i];
  281.  
  282.     /*  zero the vals array  */
  283.     if (!(i % antialias))
  284.         memset (vals, 0, widtha * sizeof (int));
  285.  
  286.     while (list)
  287.     {
  288.         x = GPOINTER_TO_INT (list->data);
  289.         list = g_slist_next (list);
  290.         if (!list)
  291.         {
  292.         g_message ("Cannot properly scanline convert polygon!\n");
  293.         }
  294.         else
  295.         {
  296.         w = GPOINTER_TO_INT (list->data) - x;
  297.  
  298.         if (w > 0)
  299.         {
  300.             if (antialias == 1)
  301.             {
  302.             channel_add_segment (mask, x, i, w, 255);
  303.             }
  304.             else
  305.             {
  306.             for (j = 0; j < w; j++)
  307.                 vals[j + x] += 255;
  308.             }
  309.         }
  310.         list = g_slist_next (list);
  311.         }
  312.     }
  313.  
  314.     if (antialias != 1 && !((i+1) % antialias))
  315.     {
  316.         b = buf;
  317.         for (j = 0; j < widtha; j += antialias)
  318.         {
  319.         val = 0;
  320.         for (x = 0; x < antialias; x++)
  321.             val += vals[j + x];
  322.  
  323.           *b++ = (unsigned char) (val / antialias2);
  324.         }
  325.  
  326.       pixel_region_set_row (&maskPR, 0, (i / antialias), sc->width, buf);
  327.     }
  328.     }
  329.  
  330.     g_free (vals);
  331.     g_free (buf);
  332.  
  333.     return mask;
  334. }
  335.  
  336.  
  337. void
  338. scan_converter_free (ScanConverter *sc)
  339. {
  340.   gint i;
  341.  
  342.   for (i = 0; i < sc->height * sc->antialias; i++)
  343.     g_slist_free (sc->scanlines[i]);
  344.  
  345.   g_free (sc->scanlines);
  346.   g_free (sc);
  347. }
  348.  
  349.  
  350. /* End of scan_convert.c */
  351.